Skip to main content

CS50x 2023

Wordle50

在这个问题中,你将会编写一个程序,它的功能和流行的 Wordle 每日单词游戏类似。


$ ./wordle 5
This is WORDLE50
You have 6 tries to guess the 5-letter word I'm thinking of
Input a 5-letter word: crash
Guess 1: crash
Input a 5-letter word: scone
Guess 2: scone
Input a 5-letter word: since
Guess 3: since
You won!

开始

打开 VS Code

首先点击你的终端窗口内部,然后单独执行 cd。 你应该发现它的“提示符”和下面显示的一样。

点击该终端窗口内部,然后执行

wget https://cdn.cs50.net/2022/fall/psets/2/wordle.zip

然后按回车键,就可以在你的 codespace 中下载一个名为 wordle.zip 的 ZIP 文件了。 注意 wget 命令和后面的 URL 之间要有一个空格,以及不要遗漏任何其他字符!

现在执行

来创建一个名为 wordle 的文件夹。 现在你可以删除这个 ZIP 文件了,执行以下命令

在提示符后输入 y 并按回车键,就可以删除你下载的 ZIP 文件了。

现在输入

然后按回车键进入(打开)这个目录。 现在你的命令提示符应该和下面显示的一样。

如果一切顺利,你应该执行

并看到一个名为 wordle.c 的文件,以及 5.txt6.txt7.txt8.txt。 执行 code wordle.c 应该会打开该文件,你将在其中键入此问题集的代码。 如果没有,请检查一下之前的步骤,看看哪里出错了! 如果你现在尝试编译游戏,它会编译而不会出错,但是当你尝试运行它时,你将看到此错误:

Error opening file 0.txt.

不过没关系,因为你还没有完成让这个错误消失的代码!

背景

如果你用 Facebook 的话,很可能见过你的朋友发过类似下面的内容,尤其是在 2022 年初 Wordle 非常火的时候:

Wordle 结果

如果是这样,你的朋友玩过 Wordle,并且分享他们当天的游戏结果! 每天都会选择一个新的“秘密单词”(对每个人都相同),目标是在六次机会内猜出这个秘密单词。 你会在游戏中得到一些提示。上面的图片展示了你的朋友如何通过这些提示,一步步猜出正确答案的。 使用类似于游戏 Mastermind 的方案,如果猜对的字母变成绿色,说明这个字母在秘密单词里,而且位置也正确。 如果变成黄色,说明这个字母在秘密单词里,但是位置不对。 灰色的字母则完全不在秘密单词里,下次就不用再猜了。 让我们一起来完成一个名为 wordle 的程序,这样我们就能在终端上重现并玩这个游戏了。我们会对游戏做一些小改动。例如,程序处理单词中重复字母的方式与原版游戏不同。但为了简单起见,我们更注重易于理解,而非完全还原游戏。此外,我们会用红色文本来表示完全不在目标词汇中的字母,而不是灰色。用户在运行程序时,需要通过命令行参数指定要猜测的单词长度,范围是 5 到 8 个字母。

下面是一些程序运行的例子:

$ ./wordle
Usage: ./wordle wordsize

如果用户提供了命令行参数,但数值不在有效范围内:

$ ./wordle 4
Error: wordsize must be either 5, 6, 7, or 8

如果用户输入参数 5,程序可能会这样运行:

$ ./wordle 5
This is WORDLE50
You have 6 tries to guess the 5-letter word I'm thinking of
Input a 5-letter word:

这时,用户需要输入一个 5 个字母的单词。当然,为了防止用户输入不符合规则的内容,我们需要确保程序能正确处理:


$ ./wordle 5
This is WORDLE50
You have 6 tries to guess the 5-letter word I'm thinking of
Input a 5-letter word: wordle
Input a 5-letter word: computer
Input a 5-letter word: okay
Input a 5-letter word: games
Guess 1: games
Input a 5-letter word:

请注意,我们甚至没有将任何无效尝试计为猜测。但是,一旦他们进行了合法的尝试,我们就将其计为猜测并报告了单词的状态。现在用户得到了一些线索:他们知道目标单词中包含字母 ae,但位置与 games 不同。同时,字母 gms 不在目标单词中,下次猜测时可以排除这些字母。也许他们可以尝试 heart 这样的词! ❤️

规范

设计并实现一个名为 wordle 的程序,完成 Wordle50 游戏的复刻。请注意,程序的大部分代码已经完成,您无需修改。您的任务是完成我们预留的七个 TODO 部分。每个部分都解决了特定的问题,我们建议您按从 1 到 7 的顺序解决它们。每个编号的 TODO 对应于下面列表中的同一项。

  1. 在第一个 TODO 中,您应该确保程序接受单个命令行参数。为了方便讨论,我们将其称为 wordsize。如果程序运行时缺少命令行参数,请打印上述错误信息并返回 1,结束程序。

  2. 在第二个 TODO 中,您需要确认 wordsize 的值是 5、6、7 或 8 之一,并将其存储起来,后续会用到。如果 wordsize 的值不是这四个数字之一,请打印上述错误信息并返回 1,结束程序。 之后,工作人员已经编写了一些代码,这些代码将打开用户想要猜测的单词长度对应的单词列表,并从 1000 个可用选项中随机选择一个。不必过于担心理解所有这些代码,这对本次作业来说不是重点。不过,我们将在以后的作业中看到类似的内容,到那时你就会更容易理解了!这是一个很好的地方停下来测试,然后再继续下一个 TODO,看看你的代码是否按预期运行。循序渐进地调试程序总是更有效率!

  3. 对于第三个 TODO,你应该帮助程序应对用户的不规范输入,确保他们的猜测是正确的长度。因此,我们需要关注 get_guess 函数,你需要完整地实现这个函数。应该提示用户(通过 get_string)输入一个 n-字母的单词(记住,该值作为参数传递给 get_guess),如果用户输入的单词长度不符合要求,应该像 Mario 游戏那样,重复提示用户输入,直到输入正确长度的单词为止。目前提供的代码没有实现这个功能,所以你需要自己完成!请注意,与真正的 Wordle 不同,我们实际上并不检查用户的猜测是否是真正的单词,因此从这个意义上说,游戏可能更容易一些。这个游戏中的所有猜测都应该是小写字符,你可以假设用户输入的所有字母都是小写字母。一旦获取到有效的猜测,就可以使用 return 语句返回该值。

  4. 接下来,对于第四个 TODO,我们需要跟踪用户在游戏中的“得分”。我们从两个层面来跟踪用户的得分:一是针对每个字母,将 2 分(定义为 EXACT)分配给位置正确的字母,1 分(定义为 CLOSE)分配给存在于单词中但位置不正确的字母,0 分(定义为 WRONG)分配给不存在于单词中的字母;二是针对整个单词,这有助于我们判断用户是否通过猜对单词而赢得了游戏。我们将在颜色编码打印时使用各个字母的分数。为了存储这些分数,我们需要一个数组,我们称之为 status。在游戏开始时,在没有进行任何猜测的情况下,它应该包含所有 0。

这是另一个停止并测试你的代码的好地方,特别是与上面的第 3 项相关!你会发现,如果此时你输入一个长度正确的单词,程序可能会显示如下结果:

Input a 5-letter word: computer
Input a 5-letter word: games
Guess 1:
Input a 5-letter word:

但这属于正常现象!实现 print_wordTODO 编号 6,所以我们不应该期望程序此时对该猜测进行任何处理。当然,你也可以添加额外的 printf 语句来进行调试(但请务必在提交代码前删除这些语句)! 5. 第五个 TODO 绝对是所有步骤里最大也可能最具挑战性的一个。在 check_word 函数中,你需要比较 guess 中的每个字母与 choice 中的每个字母(回想一下,choice 是这个游戏的“秘密单词”),并分配分数。如果字母匹配,则奖励 EXACT (2) 分并 break 跳出循环——如果已经确定字母在正确的位置,则无需继续循环。理论上,如果字母在单词中出现两次,可能会产生bug。但为了简化问题,我们暂时将其视为我们版本的特性。如果你发现字母在单词中,但不在正确的位置,则奖励 CLOSE (1) 分,但不要 break!因为该字母之后可能出现在choice的正确位置,过早break会导致用户错过。你实际上不需要在此处显式设置 WRONG (0) 分,因为你在第 4 步中已经处理了它。最终,你还需要计算单词的总分,因为该函数需要返回总分。同样,请随时使用debug50printf来调试变量值。在实现print_word之前,它们是主要的调试手段。 6. 对于第六个 TODO,你将完成 print_word 的实现。该函数应该查看你填充 status 数组的值,并使用正确的颜色代码逐个字符地打印 guess 的每个字母。你可能已经注意到文件顶部有一些#define语句(初看可能比较复杂),它们定义了ANSI颜色代码,用于改变终端字体颜色。你无需担心如何实现这四个值(GREENYELLOWREDRESET,后者只是返回到终端的默认字体)或它们的确切含义;相反,你可以直接使用它们(抽象的力量!)。另请注意,我们在分发代码中提供了一个示例,我们在游戏介绍中打印了一些绿色文本,然后重置了颜色。你可以参考以下代码,学习如何切换颜色:

printf(GREEN"This is WORDLE50"RESET"\n");

当然,与我们的示例不同,你可能不想在单词的每个字符后都打印一个换行符(相反,你只想在末尾打印一个换行符,同时重置字体颜色!),否则显示效果会是这样:


Input a 5-letter word: games
Guess 1: g
a
m
e
s
Input a 5-letter word:

  1. 最后,第七个 TODO 只是在程序终止之前进行一些整理。无论for循环因用户用完次数正常结束,还是因猜对单词提前跳出,都需要向用户报告游戏结果。如果用户确实赢得了游戏,则在此处打印一个简单的 You won! 就足够了。否则,你应该打印一条消息,告诉用户目标单词是什么,确保用户知道游戏是公正的,同时也方便你调试代码,检查是否提供了错误的提示。

如何测试你的代码

执行以下命令以使用 check50 评估代码的正确性。请务必自行编译并测试!

check50 cs50/problems/2023/x/wordle

运行以下命令,用 style50 检查你的代码风格。

如何提交

在你的终端里,运行以下命令来提交你的代码。

submit50 cs50/problems/2023/x/wordle